﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Web.Security;
using BigfootMVC.BigfootSQL;
using DotNetNuke.Common;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Services.Exceptions;
using DotNetNuke.Services.Localization;

namespace BigfootMVC.DNN
{

    /// <summary>
    /// This is the heart your DNN module application. It contains your module informaiton, as well as the methods
    /// and helpers you may use throughout your application. It also is the main source for the StaticUrl MvcUrl and ControlUrl
    /// methods that the baseview classes inherit. It also has all the functions inherited from the bigfootMVC.WebApp 
    /// you may use in all ASP.NET applications.
    /// </summary>
    public class DnnApp : WebApp
    {
        /// <summary>
        /// This is the name of the assembly/dll that contains the controllers
        /// </summary>
        public string ModuleAssemblyName;

        /// <summary>
        /// The root namespace of the module where the controllers reside)
        /// </summary>
        public string ModuleNamespace;

        /// <summary>
        /// Name of the folder where the module resides inside the DesktopModules directory
        /// </summary>
        public string ModuleFolderName;

        /// <summary>
        /// This is the short name of your module. This is the name used in the CSS files as well as the root div element
        /// on the route user control for your mvc views
        /// </summary>
        public string ModuleShortName;

        /// <summary>
        /// The database object qualifier for this module
        /// </summary>
        public string ModuleDBObjectQualifier;

        /// <summary>
        /// Retreives the view that is rendered with the error information
        /// </summary>
        public string ErrorViewPath { get { return AppPath + "error.ascx"; } }

        /// <summary>
        /// Returns the path of the current portal
        /// </summary>
        public string PortalPath { get { return PortalSett.HomeDirectory; } }

        /// <summary>
        /// Retreives the portal settings for the DNN app
        /// </summary>
        public PortalSettings PortalSett { get { return Globals.GetPortalSettings(); } }
        
        /// <summary>
        /// Inititalizes the MVC App
        /// </summary>
        /// <param name="moduleAssemblyName">This is the name of the assembly/dll that contains the controllers</param>
        /// <param name="moduleNamesapce">The root namespace of the module where the controllers reside)</param>
        /// <param name="moduleFolderName">Name of the folder where the module resides inside the DesktopModules directory</param>
        /// <param name="moduleShortName">This is the short name of your module. This is the name used in the CSS files as well as the root div element on the route user control for your mvc views</param>
        /// <param name="moduleDBObjectQualifier">The database object qualifier for this module</param>
        public DnnApp(string moduleAssemblyName, string moduleNamesapce, string moduleFolderName, string moduleShortName, string moduleDBObjectQualifier) 
            : base("~/DesktopModules/" + moduleFolderName + "/", new ASPContext(), new DnnEmailProvider())
        {
            ModuleAssemblyName = moduleAssemblyName;
            ModuleNamespace = moduleNamesapce;
            ModuleFolderName = moduleFolderName;
            ModuleShortName = moduleShortName;
            ModuleDBObjectQualifier = moduleDBObjectQualifier;
        }
        
        /// <summary>
        /// This the login url properly configured to return to the home page
        /// </summary>
        public static string LoginUrl
        {
            get
            {
                var url = FormsAuthentication.LoginUrl;
                url += url.Contains("?") ? "&" : "?";
                url += "returnurl=" + Globals.NavigateURL();
                return url;
            }
        }

        /// <summary>
        /// The url for the portal's home page        /// </summary>
        public static string HomeUrl { get { return Globals.NavigateURL(); } }
        
        /// <summary>
        /// Provides the application with access to the DataProvider in order to run queries etc.
        /// </summary>
        public DataProvider DB
        {
            get { return _db ?? (_db = new DataProvider(ModuleDBObjectQualifier, ModuleShortName)); }
            set { _db = value; }
        }
        private DataProvider _db;

        /// <summary>
        /// Returns a SqlHelper class already configured to execute queries against the database
        /// </summary>
        public SqlHelper SQL
        {
            get
            {
                return new SqlHelper(DB.DatabaseOwner, 
                                     DB.ObjectQualifier, 
                                     DB.ModuleQualifier, 
                                     DB.ConnectionString);
            }
        }
        
        /// <summary>
        /// Gets a browser usable path to a MVC controller action.
        /// </summary>
        /// <param name="controller">The name of the controller that contains the action</param>
        /// <param name="action">The name of the action within the controller</param>
        /// <param name="moduleId">The module id to include in the url route</param>
        /// <param name="querystring">Appends this value to the querystring </param>
        /// <param name="fullUrl">Determines weather to append the domain name to the url</param>
        /// <param name="portalId">The portal id to include in the url route</param>
        /// <param name="tabId">The tab id to include in the url route</param>
        /// <returns>A url that may be used from the browser</returns>
        public string MvcUrl(string controller, string action, int portalId, int tabId, int moduleId, string querystring, bool fullUrl)
        {
            var relurl = Globals.NavigateURL(tabId, "", BuildUrlRoute(Params.QS.Route(moduleId), controller, action, portalId, tabId, moduleId, querystring).ToArray());
            if (fullUrl && relurl.StartsWith("http") == false)
            {
                relurl = GetAbsoluteUrl(relurl, true);
            }
            return relurl;
        }

        /// <summary>
        /// Gets a browser usable path to a MVC controller action for AJAX requests. It activates the ajax route handler.
        /// </summary>
        /// <param name="controller">The name of the controller that contains the action</param>
        /// <param name="action">The name of the action within the controller</param>
        /// <param name="moduleId">The module id to include in the url route</param>
        /// <param name="querystring">Appends this value to the querystring </param>
        /// <param name="portalId">The portal id to include in the url route</param>
        /// <param name="tabId">The tab id to include in the url route</param>
        /// <returns>A url that may be used from the browser to make an ajax request</returns>
        public string MvcAjaxUrl(string controller, string action, int portalId, int tabId, int moduleId, string querystring)
        {
            var qs = BuildUrlRoute(Params.QS.AjaxRoute(ModuleShortName), controller, action, portalId, tabId, moduleId, querystring);
            var relurl = AppPath + "route.ashx?" + String.Join("&", qs.ToArray());
            if (relurl.StartsWith("~/"))
                relurl = relurl.Substring(2);
            if (relurl.EndsWith("/"))
                relurl = relurl.Substring(0, relurl.Length - 1);
            return GetApplicationPath() + relurl;
        }

        /// <summary>
        /// Receives the route data from the url and converts it into a RouteInfo object
        /// </summary>
        /// <param name="routeData">The route data from the url</param>
        /// <param name="moduleBase">If loading from an existing PortalModuleBase use it, otherwise create a new one</param>
        public RouteInfo DecodeUrlRoute(string routeData, PortalModuleBase moduleBase)
        {
            var routeVars = routeData.Split('-');

            // Validate the route data parameter
            if (routeVars.Length != 5) throw new ApplicationException("Invalid route: " + routeData);

            var controller = routeVars[0];
            var action = routeVars[1];
            var tabId = int.Parse(routeVars[2]);
            var portalId = int.Parse(routeVars[3]);
            var moduleId = int.Parse(routeVars[4]);

            var modController = new ModuleController();

            // Load the module base if not supplied
            if (moduleBase == null)
                moduleBase = new PortalModuleBase { ModuleConfiguration = modController.GetModule(moduleId, tabId) };
            
            var route = new RouteInfo(controller, action, moduleBase, portalId, this);
            return route;
        }

        /// <summary>
        /// Creates the Route data to be included in the URL Querystring. It inlcudes the parameter name.
        /// </summary>
        /// <param name="paramName">The name of the parameter to use in the querystring</param>
        /// <param name="controller">The name of the controller that contains the action</param>
        /// <param name="action">The name of the action within the controller</param>
        /// <param name="moduleId">The module id to include in the url route</param>
        /// <param name="querystring">Appends this value to the querystring </param>
        /// <param name="portalId">The portal id to include in the url route</param>
        /// <param name="tabId">The tab id to include in the url route</param>
        /// <returns>A url that may be used from the browser to make an ajax request</returns>
        private List<string> BuildUrlRoute(string paramName, string controller, string action, int portalId, int tabId, int moduleId, string querystring)
        {

            // Append any other routes currently in this page

            // Build the route data
            var routeData = string.Format("{0}-{1}-{2}-{3}-{4}",
                                           controller,
                                           action,
                                           tabId,
                                           portalId,
                                           moduleId);

            var qs = new List<string>();
            //qs.Add(routeData);
            const string paramFormat = "{0}={1}";

            // Add the route parameter
            qs.Add(string.Format(paramFormat, paramName, routeData));

            // Add the querystring parameters
            var portalIdInQueryString = false;
            if (!string.IsNullOrEmpty(querystring))
            {
                foreach (var s in querystring.Split('&'))
                {
                    var qsItem = s.Split('=');
                    if (string.IsNullOrEmpty(s)) continue;
                    qs.Add(qsItem.Length > 1 ? string.Format(paramFormat, qsItem[0], qsItem[1]) :
                                               string.Format(paramFormat, s, ""));
                    // Check whether the portalid is in the query string
                    if (qsItem[0].ToLowerInvariant() == "portalid") portalIdInQueryString = true;
                }
            }

            // Add any other route paramters currently in the request. When in AjaxRequest mode, although these are not used
            // it makes calls to the MvcUrl or AjaxUrl functions from views being executed within an Ajax Request keep their 
            // place for other modules within the page. Be it the same or another module also using BigfootMVC
            foreach (var qp in Request.QueryString.AllKeys)
            {
                if (!qp.ToLower().StartsWith("route_") || qp.Equals(paramName)) continue;
                
                var value = Request.QueryString[qp];
                qs.Add(string.Format(paramFormat, qp, value));
            }

            // Always add the portalid for ajax requests. 
            // Otherwise it logs out the user so the request won't be authenticated even though the user has logged in
            if (paramName == Params.QS.AjaxRoute(ModuleShortName) && !portalIdInQueryString)
            {
                qs.Add(string.Format(paramFormat, "portalid", portalId));
            }

            return qs;
        }

        /// <summary>
        /// This is a shortcut to DotNetNuke's NavigateUrl. It gets the url path to activate a certain DNN control within your module.
        /// </summary>
        /// <param name="moduleId">The ModuleID for the module containing the control</param>
        /// <param name="controlName">The control key within your module delcaration</param>
        /// <param name="queryString">Appends this value to the querystring </param>
        /// <returns>A url that may be used from the browser</returns>
        public string ControlUrl(int moduleId, string controlName, string queryString)
        {
            queryString += string.IsNullOrEmpty(queryString) ? "mid=" + moduleId : "&mid=" + moduleId;
            return Globals.NavigateURL(controlName, queryString);
        }
        
        /// <summary>
        /// Handle an exception. It logs it to DNN and knows how to handle security exceptions on ajax requests etc.
        /// </summary>
        /// <param name="ex">The exception to handle</param>
        public void HandleException(Exception ex)
        {
            HandleException(ex, true);
        }

        /// <summary>
        /// Handle an exception. It logs it to DNN and knows how to handle security exceptions on ajax requests etc.
        /// </summary>
        /// <param name="ex">The exception to handle</param>
        /// <param name="ReThrow">Determines weather to rethrow the exception after the loging actions etc. have been executed</param>
        public void HandleException(Exception ex, bool ReThrow)
        {
            // Ignore exceptions due to response manually ended
            if (ResponseEndCalled) { return; }

            // Log the exception in DNN
            Exceptions.LogException(ex);

            // When an ajax request, signal to the client that the request failed.
            if (IsAjaxRequest)
            {
                Response.Clear();

                RenderViewToResponse(ErrorViewPath, ErrorViewModel.Create(ex, Request.Url.ToString()));

                // Set the status code to 402 for security problems. 
                //  This is needed in ajax request in order to allow the javascript to respond by redirecting the user
                //  from the browser. Otherwise the asp.net authentication module will do a redirect and render back to the
                //  ajax call the login page, which would break your design since you might be injecting whatever 
                //  comes back into an element in the existing page. BigfootMVC Javascript sees the error code 402 and it
                //  knows that it needs to do a redirect client side to the login page.
                if ((ex.GetType().Name == typeof(System.Security.SecurityException).Name) || 
                    (ex.InnerException != null && object.ReferenceEquals(ex.InnerException.GetType().Name, typeof(System.Security.SecurityException).Name)))
                {
                    Response.StatusCode = 402;
                }
                else
                {
                    Response.StatusCode = 500;
                }

                Response.StatusDescription = ex.Message;
                Response.End();
            }
            else if (ReThrow)
            {
                throw ex;
            }
        }

        ///// <summary>
        ///// Registers the jQuery, jQuery.UI, jQuery.Validate, jQuery.form, jQuery.values, 
        ///// jQuery.Tools[overlay, flashembed, expose], jQuery.centerXY, jQuery.pagination, and bigfootMVC.
        ///// It registers them from the google CDN (and the BigfootMVC.js from local) with the smoothness theme by default.
        ///// </summary>
        //public string RegisterBigfootMVCScripts()
        //{
        //    return RegisterBigfootMVCScripts(true, "smoothness", true, false);
        //}

        ///// <summary>
        ///// Registers the jQuery, jQuery.UI, jQuery.Validate, jQuery.form, jQuery.values, 
        ///// jQuery.Tools[overlay, flashembed, expose], jQuery.centerXY, jQuery.pagination, and bigfootMVC.
        ///// It registers them from the google CDN (and the BigfootMVC.js from local) with the smoothness theme by default. 
        ///// </summary>
        ///// <param name="useCDN">Determines whether to use the Google CDN to load jQuery and jQueryUI</param>
        //public string RegisterBigfootMVCScripts(bool useCDN)
        //{
        //    return RegisterBigfootMVCScripts(useCDN, "smoothness", !useCDN, false);
        //}

        ///// <summary>
        ///// Registers the jQuery, jQuery.UI, jQuery.Validate, jQuery.form, jQuery.values, 
        ///// jQuery.Tools[overlay, flashembed, expose], jQuery.centerXY, jQuery.pagination, and bigfootMVC.
        ///// </summary>
        ///// <param name="useCDN">Determines whether to use the Google CDN to load jQuery and jQueryUI</param>
        ///// <param name="jQueryUITheme">
        ///// The name of the theme to load. Must correspond to a folder at css/jquery/[themename]/jquery-ui.css
        ///// if loading from the CDN then it must be one of the theme standard jQueryUI themes. 
        ///// Use - {dash} to replace any spaces on the standard theme names.
        ///// If you would like to use a different theme locally you must make sure it is named located in 
        ///// css/jquery/[nameoftheme]/jquery-ui.css. If you create a custom theme you must load it locally,
        ///// you are not able to use the CDN feature to load the jQueryUI as your theme would not be loaded on the 
        ///// Google CDN
        ///// </param>
        //public string RegisterBigfootMVCScripts(bool useCDN, string jQueryUITheme)
        //{
        //    return RegisterBigfootMVCScripts(useCDN, jQueryUITheme, !useCDN, false);
        //}

        ///// <summary>
        ///// Registers the jQuery, jQuery.UI, jQuery.Validate, jQuery.form, jQuery.values, 
        ///// jQuery.Tools[overlay, flashembed, expose], jQuery.centerXY, jQuery.pagination, and bigfootMVC.
        ///// </summary>
        ///// <param name="useCDN">Determines whether to use the Google CDN to load jQuery and jQueryUI</param>
        ///// <param name="jQueryUITheme">
        ///// The name of the theme to load. Must correspond to a folder at css/jquery/[themename]/jquery-ui.css
        ///// if loading from the CDN then it must be one of the theme standard jQueryUI themes. 
        ///// Use - {dash} to replace any spaces on the standard theme names.
        ///// If you would like to use a different theme locally you must make sure it is named located in 
        ///// css/jquery/[nameoftheme]/jquery-ui.css. If you create a custom theme you must load it locally,
        ///// you are not able to use the CDN feature to load the jQueryUI as your theme would not be loaded on the 
        ///// Google CDN
        ///// </param>
        ///// <param name="useLocalTheme">Tells the system to use the local theme for jQuery.UI</param>
        //public string RegisterBigfootMVCScripts(bool useCDN, string jQueryUITheme, bool useLocalTheme)
        //{
        //    return RegisterBigfootMVCScripts(useCDN, jQueryUITheme, useLocalTheme, false);
        //}

        ///// <summary>
        ///// Registers the jQuery, jQuery.UI, jQuery.Validate, jQuery.form, jQuery.values, 
        ///// jQuery.Tools[overlay, flashembed, expose], jQuery.centerXY, jQuery.pagination, and bigfootMVC.
        ///// </summary>
        ///// <param name="useCDN">Determines whether to use the Google CDN to load jQuery and jQueryUI</param>
        ///// <param name="jQueryUITheme">
        ///// The name of the theme to load. Must correspond to a folder at css/jquery/[themename]/jquery-ui.css
        ///// if loading from the CDN then it must be one of the theme standard jQueryUI themes. 
        ///// Use - {dash} to replace any spaces on the standard theme names.
        ///// If you would like to use a different theme locally you must make sure it is named located in 
        ///// css/jquery/[nameoftheme]/jquery-ui.css. If you create a custom theme you must load it locally,
        ///// you are not able to use the CDN feature to load the jQueryUI as your theme would not be loaded on the 
        ///// Google CDN
        ///// </param>
        ///// <param name="checkFirst">Determines whether to perform a check before loading the scripts. Required advanced knowledge of JS dependencies</param>
        ///// <param name="useLocalTheme">Tells the system to use the local theme for jQuery.UI</param>
        //public string RegisterBigfootMVCScripts(bool useCDN, string jQueryUITheme, bool useLocalTheme, bool checkFirst)
        //{
        //    if (!useCDN || useLocalTheme) jQueryUITheme = StaticUrl("jquery/" + jQueryUITheme + "/jquery-ui.css");
        //    var script = new StringBuilder();
        //    script.AppendLine(Html.LoadJQuery(useCDN, StaticUrl("jquery-1.4.4.min.js"), checkFirst));
        //    script.AppendLine(Html.LoadJQueryUI(useCDN, StaticUrl("jquery-ui-1.8.7.min.js"), jQueryUITheme, useLocalTheme, checkFirst));
        //    script.AppendLine(Html.LoadBigfootMVCScript(StaticUrl("bigfootMVC.js"), checkFirst));
        //    return script.ToString();
        //}

        /// <summary>
        /// Gets a localized string.
        /// </summary>
        /// <param name="key">The key to retrieve. DotNetNuke by default adds .Text at the end of any
        /// key that does not contain a "." dot in it. To avoid this behaviour structure your
        /// key names with a "." dot in them.
        /// </param>
        /// <param name="moduleFolderName">The name of the module containing the resource file</param>
        /// <returns>Returns null if not found otherwise the value</returns>
        public static string GetLocalString(string key, string moduleFolderName)
        {
            return GetLocalString(key, moduleFolderName, "");
        }

        /// <summary>
        /// Gets a localized string.
        /// </summary>
        /// <param name="key">The key to retrieve. DotNetNuke by default adds .Text at the end of any
        /// key that does not contain a "." dot in it. To avoid this behaviour structure your
        /// key names with a "." dot in them.
        /// </param>
        /// <param name="moduleFolderName">The name of the module containing the resource file</param>
        /// <param name="defaultValue">The default value to return if the requested value is not found</param>
        /// <returns>Returns null if not found otherwise the value</returns>
        public static string GetLocalString(string key, string moduleFolderName, string defaultValue)
        {
            if (string.IsNullOrEmpty(key)) return defaultValue;
            var value = Localization.GetString(key, "~/DesktopModules/" + moduleFolderName + "/App_LocalResources/SharedResources.resx");
            return string.IsNullOrEmpty(value) ? defaultValue : value;
        }

        
//        /// <summary>
//        /// Outputs the appropriate Javascript and CSS references needed by BigfootMVC
//        /// </summary>
//        /// <returns>The html needed to include in the mvc page</returns>
//        public string AddBigfootScriptAndCssDependencies()
//        {
//            /*
//            <%= HtmlHelper.JSReference(Route.App.StaticUrl("req.js"))%>
//            <%= HtmlHelper.CSSReference(Route.App.StaticUrl("bigfootMVC.css"))%>

//            <%--EXECUTE SCRIPTS--%>
//            <script type="text/javascript">    
//                // Global variables
//                var loginurl = "<%= Route.App.LoginUrl%>";

//                // Load the scripts as needed
//                Req.baseUrl = "<%= Route.App.StaticUrl(Route.App.ScriptsPath, true)%>";
//                Req('bigfootMVC', function() { bigfootMVC.connectAjax(); } );
//            </script>
//             */
//            var script = new StringBuilder();
//            script.AppendLine(Html.JSReference(StaticUrl("req.js")));
//            script.AppendLine(Html.CSSReference(StaticUrl("bigfootMVC.css")));
//            script.AppendFormat(@"
//                <script type='text/javascript'>    
//                    var loginurl = '{0}';
//                    Req.baseUrl = '{1}';
//                    Reg.RegisterDependencies({1});
//                    //Req('bigfootMVC', function() {{ bigfootMVC.connectAjax(); }} );
//                </script>",
//                LoginUrl, 
//                StaticUrl(ScriptsPath, true));

//            // Return the script
//            return script.ToString();
//        }

        public static string XmlSerialize(object data)
        {
            var serializer = new SoapFormatter();
            using(var ms = new MemoryStream())
            {
                serializer.Serialize(ms, data);
                ms.Flush();
                ms.Position = 0;
                using (var sr = new StreamReader(ms))
                {
                    return sr.ReadToEnd();
                }
                
            }
        }

        
        public static object XmlDeserialize(string data) {
            var serializer = new SoapFormatter();
            using (var ms = new MemoryStream())
            {
                using (var sw = new StreamWriter(ms))
                {
                    sw.Write(data);
                    sw.Flush();
                    ms.Position = 0;
                    return serializer.Deserialize(ms);
                }

            }
        }

    }
}
